/*
 *-------------------------------------------------------------------
 *
 * BUCKET.C
 *
 * Code for manipulating the buckets linked list
 * This code is NOT totally implementation-independent
 * the routines are written for the kind of data
 * they are manipulating
 * 
 * Marcelo Walter
 *
 *-------------------------------------------------------------------
 */

/*
 *-------------------------------------------------------------------
 *   INCLUDES
 *-------------------------------------------------------------------
 */
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "common.h"

/*
 *-----------------------------------------------------------------
 *
 * 	EXTERN GLOBAL VARIABLES
 *
 *-----------------------------------------------------------------
 */
extern CELL 	*headC; /* declared in list.c */
extern CELL 	*zedC;  /* declared in list.c */

/* size of buckets. Declared in main.c */
extern float 	sizeX, sizeY;
/*
 *---------------------------------------------------------------
 *
 * GLOBAL VARIABLES
 *
 *---------------------------------------------------------------
 */
/* head and tail nodes of the bucket linked list */
BUCKET *head[BUCKET_SIZE_X][BUCKET_SIZE_Y];
BUCKET *zed[BUCKET_SIZE_X][BUCKET_SIZE_Y];
int bucket_size = sizeof(BUCKET);

/*
 *-----------------------------------------------------------
 * Free the space used by the buckets
 *-----------------------------------------------------------
 */
void free_buckets_list(int sx, int sy)
{
  int i,j;
  BUCKET *t,*to_remove;

  for(i=0; i < sx;i++)
  {
    for(j=0; j < sy;j++)
    {
      t=head[i][j]->next;
      while(t != zed[i][j])
      {
        to_remove = t;
        t = to_remove->next;
        remove_bucket(to_remove,head[i][j],zed[i][j]);
      }
    }
  }
}

/*
 *-----------------------------------------------------------------------
 *	For a particular cell located at (x,y)
 *	finds its bucket.
 *	INPUT: 	the cell's coordinates (x,y)
 *		the size of the bucket (sx,sy)
 *
 *	OUTPUT:	the cell's bucket indexes
 *	returned through (ix,iy)
 *-----------------------------------------------------------------------
 */
void findBucket(double x, double y, int *ix, int *iy)
{
  *ix = (int) floor(x/sizeX);
  *iy = (int) floor(y/sizeY);
}

/*
 *-----------------------------------------------------------------------
 *	Assign cells to buckets
 *-----------------------------------------------------------------------
 */
void assign_buckets(void)
{
  BUCKET *bucket;
  int ix,iy;
  CELL *cell;

  cell = headC->next;
  while( cell != zedC ){
    
    /*ix = floor(s->x/rad);
      iy = floor(s->y/rad);*/
    
    //findBucket(s->x,s->y,&ix,&iy);
    ix = (int) floor(cell->x/sizeX);
    iy = (int) floor(cell->y/sizeY);
    
    /* insert cell information into the bucket */
    bucket = balloc();
    bucket->info = cell;
    insert_bucket( bucket, head[ix][iy], zed[ix][iy] );
    cell = cell->next;
  }
}

/*
 *-----------------------------------------------------------
 * Print the bucket list
 *-----------------------------------------------------------
 */

void print_bucket_list(BUCKET *h, BUCKET *t)
{
  BUCKET *s;

  s = h->next;
  while(s != t)
  {
    printf( "x = %f y = %f\n", (s->info)->x, (s->info)->y );
    /*printf("fx = %f fy = %f\n",(s->info)->fx,(s->info)->fy);*/
    s = s->next;
  }
}

/*
 *-----------------------------------------------------------------------
 *  Print buckets
 *-----------------------------------------------------------------------
 */
void print_buckets(int sx, int sy)
{
  int i,j;

  for(i = 0; i < sx; i++)
  {
    for(j = 0; j < sy; j++)
    {
      if( (head[i][j])->next != zed[i][j] )
      {
        printf("Bucket[%d][%d] :\n", i, j);
        print_bucket_list( head[i][j], zed[i][j] );
        printf("\n");
      }
    }
    printf("\n");
  }

}


/* 
 *-----------------------------------------------------------
 * Creates the bucket list. Initialize heads and
 * zeds (dummy node). Head and zed are global
 * variables (I might change this later on so that
 * I will only initialize the correct number of
 * buckets and not an array bigger than necessary)
 *---------------------------------------------------------------
 */
void create_bucket_list(void)
{
  int i,j;

  for(i = 0; i < BUCKET_SIZE_X; i++)
  {
    for(j = 0; j < BUCKET_SIZE_Y; j++)
    {
      head[i][j] = balloc();
      zed[i][j]  = balloc();
      (head[i][j])->next = zed[i][j];
      (head[i][j])->info = (CELL *)NULL;
      (zed[i][j])->next  = (BUCKET *)NULL;
      (zed[i][j])->info  = (CELL *)NULL;
    }
  }
}

/*
 *-----------------------------------------------------------
 * Allocate space for a new node and returns
 * a pointer to the newly allocated node.
 * If malloc cannot find space the routine
 * exits.
 *-----------------------------------------------------------
 */
BUCKET *balloc(void)
{
  BUCKET *new;
  
  if((new = (BUCKET *)malloc(bucket_size)) == NULL)
      errorMsg("Error with malloc in function balloc()!");
  
  new->next = (BUCKET *)NULL;
  return new;
}


/*
 *-----------------------------------------------------------
 *  Inserts a node in the list
 *  Receives a pointer to the node to be
 *  inserted, a pointer to the head
 *  node of the list and to the
 *  tail node.
 *  Inserts bucket p in the end of the list.
 *-----------------------------------------------------------
 */
void insert_bucket(BUCKET *p, BUCKET *head, BUCKET *tail)
{
  BUCKET *s;

  s = head;
  while(s->next != tail) s = s->next;
  
  s->next = p;
  p->next = tail;
}

/*
 *-----------------------------------------------------------
 *  Removes a node from the list.
 *  Receives a pointer to the head of the
 *  list and to the node to be removed
 *-----------------------------------------------------------
 */
void remove_bucket(BUCKET *p, BUCKET *h, BUCKET *t)
{
  BUCKET *s;
  
  if(h->next == t)
      errorMsg("Trying to remove an element for an empty bucket list...");
  
  s = h;
  while(s->next != p) s = s->next;
  
  s->next = p->next;
  free(p);
}

//declared in main.cu
extern int bx, by;
extern float max_x, max_y;
extern double nradius;

/*
 *------------------------------------------------------------------------
 *	rearrange buckets
 *	When the number of cells is 50% greater than
 *	the previous quantity, we re-arrange the bucket
 *	information based on the new number of cells
 *------------------------------------------------------------------------
 */
void rearrangeBuckets(void)
{
  free_buckets_list( bx, by );

  /* bx and by are the number of buckets in the x and y directions */
  bx = (int) max_x / ceil(nradius);
  by = (int) max_y / ceil(nradius);

  /* This is giving a bucket size slightly smaller
     than the radius size. I think the bucket size
     should be at least the same as the repulsive
     radius */
  sizeX = max_x / (float) (bx);
  sizeY = max_y / (float) (by);

  assign_buckets();

  printf("\nBuckets reorganized...\n");
  printf("New radius = %2.2f\n",nradius);
  printf("Number of buckets in x = %f and y = %f\n",bx,by);
  printf("Size of buckets in x = %2.3f and y = %2.3f\n\n",sizeX,sizeY);
}
